using System;
using System.Reflection;
using System.Reflection.Emit;
using Live2D.Cubism.Core.Unmanaged.ReflectorOptimization.Common;
using UnityEngine;
using UnityEngine.Internal;
using CxExtension;

namespace Live2D.Cubism.Core.Unmanaged
{
	public enum MeshShaderChannel
	{
		Vertex,
		Normal,
		Color,
		TexCoord0,
		TexCoord1,
		TexCoord2,
		TexCoord3,
		Tangent,
	}

	public enum MeshVertexChannelType
	{
		Float = 0,
		Color = 2,
	}
	public static class MeshExt
	{
		public delegate void SetTrianglesD(Mesh self, int submesh, Array triangles, int arraySize, bool calculateBounds);

		public delegate void SetArrayForChannelImplD(Mesh self,
			int channel,
			int format,
			int dim,
			Array values,
			int valuesCount);
		private static SetTrianglesD mSetTrianglesImplAction5;                      //
		private static SetArrayForChannelImplD mSetArrayForChannelImpl5;                      //
		private static MethodInfo mSetArrayForChannelMethod5;                      //
		private static object[] mSetArrayForChannelMethodParams5 = new object[5];                      //
		//private static FastMethodInvoker.FastInvokeHandler mSetTrianglesImplAction5;
		
		static MeshExt()
		{
			//mSetTrianglesImplAction5
			Init();
		}

		public static void Init()
		{
			var type = typeof(Mesh);
			var delegateType = typeof(SetTrianglesD);
			var types = new[] { typeof(int), typeof(Array), typeof(int), typeof(bool) };
			var bindFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
			//var methodInfos = type.GetMethods(bindFlags);
			var methodInfo = type.GetMethod("SetTrianglesImpl", bindFlags, null, types, null);
			mSetTrianglesImplAction5 = (SetTrianglesD)Delegate.CreateDelegate(delegateType, methodInfo, true);
			methodInfo = type.GetMethod("SetArrayForChannelImpl",bindFlags, 5);
			mSetArrayForChannelMethod5 = methodInfo;
			//mSetArrayForChannelImpl5 = (SetArrayForChannelImplD)Delegate.CreateDelegate(typeof(SetArrayForChannelImplD), //methodInfo, true);

		}
		public static void SetArrayForChannelImpl(this Mesh self,
			MeshShaderChannel channel,
			MeshVertexChannelType format,
			Array values,
			int arraySize)

		{
			mSetArrayForChannelMethodParams5[0] = (int)channel;
			mSetArrayForChannelMethodParams5[1] = (int)format;
			mSetArrayForChannelMethodParams5[2] = DefaultDimensionForChannel(channel);
			mSetArrayForChannelMethodParams5[3] = values;
			mSetArrayForChannelMethodParams5[4] = arraySize;
			mSetArrayForChannelMethod5.Invoke(self,mSetArrayForChannelMethodParams5);
		}
		public static void SetVertices(this Mesh self, Array values, int arraySize)
		{
			self.SetArrayForChannelImpl(MeshShaderChannel.Vertex,MeshVertexChannelType.Float,values,arraySize);
		}
		public static void SetColors(this Mesh self, Array values, int arraySize)
		{
			self.SetArrayForChannelImpl(MeshShaderChannel.Color, MeshVertexChannelType.Float, values, arraySize);
		}
		public static void SetTrianglesE(this Mesh self,int submesh , Array triangles, int arraySize, bool calculateBounds)
		{
			mSetTrianglesImplAction5(self, submesh,triangles,arraySize,calculateBounds);
		}
		public static void SetTrianglesE(this Mesh self, Array triangles, int arraySize, bool calculateBounds)
		{
			mSetTrianglesImplAction5(self, -1, triangles, arraySize, calculateBounds);
		}
		internal static int DefaultDimensionForChannel(MeshShaderChannel channel)
		{
			if (channel == MeshShaderChannel.Vertex || channel == MeshShaderChannel.Normal)
				return 3;
			if (channel >= MeshShaderChannel.TexCoord0 && channel <= MeshShaderChannel.TexCoord3)
				return 2;
			if (channel == MeshShaderChannel.Tangent || channel == MeshShaderChannel.Color)
				return 4;
			throw new ArgumentException("DefaultDimensionForChannel called for bad channel", (channel.ToString()));
		}
	}
}